package sky.cin.framework.sfoc.advanced.mvc;

import com.alibaba.fastjson.JSONObject;
import sky.cin.framework.log.Croog;
import sky.cin.framework.network.dto.HttpServletRequest;
import sky.cin.framework.network.dto.HttpServletResponse;
import sky.cin.framework.network.dto.Servlet;
import sky.cin.framework.sfoc.core.annotations.https.RequestParam;

import java.lang.annotation.Annotation;
import java.lang.reflect.Method;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

public class DispatcherServlet implements Servlet {

    private static final String HOST = "\\w+://\\w+:\\d+";
    private static final String IP = "\\w+://\\d+.\\d+.\\d+.\\d+:\\d+";
    private static Croog log = new Croog("sky.cin.framework.sfoc.advanced.mvc.DispatcherServlet");
    private static final Map<String, RequestHandler> controllerMapping = new ConcurrentHashMap<>();

    public static void put(String key, RequestHandler value) {
        controllerMapping.put(key, value);
    }
    
    public void doGet(HttpServletRequest req, HttpServletResponse resp) {
        String url = req.getRequestURL()
                .replaceAll(HOST, "")
                .replaceAll(IP, "");
        if ("/mvc/interface".equals(url)) {
            resp.getWriter().write(JSONObject.toJSONString(controllerMapping));
            return;
        }
        log.info(String.format("%s --> %s", req.getHeader("host"), url));
        // 根据路径获取到 controller
        RequestHandler bean = controllerMapping.get(url);
        if (bean == null) {
            resp.NotFound404(url);
            return;
        }
        try {
            // 解析参数
            Object[] args = argumentsHandler(req, resp, bean.getMethod());
            // 调用并获取结果
            bean.getMethod().setAccessible(true);
            Object invokeResult = bean.getMethod().invoke(bean.getBean(), args);
            String result = invokeResult == null ? "null" : invokeResult.toString();
            if (canJson(invokeResult)) {
                result = JSONObject.toJSONString(invokeResult);
            }
            resp.getWriter().write(result);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
    
    public static boolean canJson(Object obj) {
        return !(obj instanceof Character || obj instanceof Byte || obj instanceof Short ||
            obj instanceof Boolean || obj instanceof Integer || obj instanceof Long ||
            obj instanceof Float || obj instanceof Double || obj instanceof String);
    }
    
    @Override
    public void doPost(HttpServletRequest req, HttpServletResponse resp) {
    }
    
    private Object[] argumentsHandler(HttpServletRequest request, HttpServletResponse response, Method method) {
        Class<?>[] parameterTypes = method.getParameterTypes();
        Object[] args = new Object[parameterTypes.length];
        for (int index = 0, arg_i = 0; index < parameterTypes.length; index++) {
            // 判断参数是否为 Request
            if (HttpServletRequest.class.isAssignableFrom(parameterTypes[index])) {
                args[arg_i++] = request;
            }
            // 判断参数是否为 Response
            if (HttpServletResponse.class.isAssignableFrom(parameterTypes[index])) {
                args[arg_i++] = response;
            }
            // 获取第 index 个参数的注解列表
            Annotation[] parameterAnnotations = method.getParameterAnnotations()[index];
            if (parameterAnnotations.length <= 0) {
                continue;
            }
            for (Annotation parameterAnnotation : parameterAnnotations) {
                // 解析 RequestParam，从 URL 读取值
                if (RequestParam.class.isAssignableFrom(parameterAnnotation.getClass())) {
                    RequestParam requestParam = (RequestParam) parameterAnnotation;
                    String argName = requestParam.value();
                    String parameterValue = request.getParameter(argName);
                    args[arg_i++] = resolve(parameterValue);
                }
            }
        }
        return args;
    }
    
    private Object resolve(String parameterValue) {
        try {
            return Integer.parseInt(parameterValue);
        } catch (Exception e) {
            return parameterValue;
        }
    }
    
}
